/*
 * Copyright 2007 Google Inc.
 * Licensed under the Apache License, Version 2.0:
 *    http://www.apache.org/licenses/LICENSE-2.0
 */

// Time constants
var SECONDS_IN_DAY = (60 * 60 * 24);
var SECONDS_IN_HOUR = (60 * 60);
var SECONDS_IN_MINUTE = 60;

// TODO: Pull the status out server side.
// Colors for each fire status
var Severity = {
  "SAFE": 0,
  "Status: Patrol": 0,
  "CONTROLLED": 1,
  "STATUS: Under Control": 1,
  "Status: Contained": 2,
  "CONTAINED": 2,
  "STATUS: Being Controlled": 2,
  "Status: Being controlled": 2,
  "GOING": 3,
  "Status: Going": 3,
  "STATUS: Out of Control": 3
};

// Sizes for clustered markers (not currentl used)
var Sizes = {
  'SMALL' : 9,
  'MEDIUM' : 12,
  'LARGE' : 16
};

// Mapping from color to severity
// HACK(jmcgill)
var SeverityToColor = [
  '00FF00', 'FFCC00', 'FF9912', 'FF0000'
];

// Global map instance.
var map;
var map_bounds = new google.maps.LatLngBounds();
var current_infowindow = null;

// Used to avoid zooming the map after the user has interacted with it.
var loaded_before = false;

/*
 * Initialize the map when the site is first loaded. Zooms in on Australia.
 */
function initialize() {
  var latlng = new google.maps.LatLng(-37.431251, 145.030518);
  var mapOptions = {
    zoom: 8,
    center: latlng,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };

  var mapCanvas = document.getElementById("map_canvas");
  map = new google.maps.Map(mapCanvas, mapOptions);
  updateData();
}

/*
 * Show the legend.
 */
function showInfo(id) {
  var element = document.getElementById(id);
  element.style.display = 'block';
}

function hideInfo(id) {
  var element = document.getElementById(id);
  element.style.display = 'none';
}

/*
 * Request a new copy of the data from the RSS feed.
 */
function updateData() {
  var request;
  try {
    request = new ActiveXObject("MSXML2.XMLHTTP");
  } catch (NotIE5) {
    try {
      request = new ActiveXObject("Microsoft.XMLHTTP");
    } catch (NotIE6) {
      request = new XMLHttpRequest();
    }
  }

  request.onreadystatechange = function() {
    if (request.readyState == 4) {
      // We expect a JSON object in return. Safe(ish) to eval it.
      // TODO(jmcgill): Move to JSONP (is it worth it for not x-platform?)
      eval('response = ' + request.responseText);
      parseFeed(response.channel);

      if (!loaded_before) {
        map.fitBounds(map_bounds);
        loaded_before = true;
      }

      // Schedule another load of the data in 5 minutes
      // (5 minutes x 1000 milliseconds)
      window.setTimeout(updateData, 5 * 60 * 1000);
    }
  }

  request.open("GET", "/query/fire?agency=merged&fmt=json");
  request.send("");
}

/*
 * Parse the RSS feed, updating the locations of fires.
 */
function parseFeed(feed) {
  var approximate_locations = [];
  var items_length = feed.items.length;

  for (var i = 0; i < items_length; ++i) {
    var item = feed.items[i];

    // TODO(jmcgill): Unmapped fires

    var latlng_split = item.point.text.split(' ');

    // Correct reversed lat,lngs. This will only work for points below
    // the 100th meridian (i.e. Australia).
    var latlng;
    if (latlng_split[0] > 100) {
      latlng = new google.maps.LatLng(latlng_split[1], latlng_split[0]);
    } else {
      latlng = new google.maps.LatLng(latlng_split[0], latlng_split[1]);
    }

    // If this is an approximate geocode (town level, instead of street
    // level) then it may appear multiple times. Results are cached
    // to render later.
    if (item.featuretypetag && item.featuretypetag.text == "area") {
      if (!approximate_locations[item.point.text]) {
        // TODO(jmcgill): This could be smaller.
        approximate_locations[item.point.text] = {
          latlng: latlng,
          entries: [],
          name: item.title.text
        };
      }
      approximate_locations[item.point.text].entries.push(item);
    } else {
      var severity = findInText(Severity, item.description.text) || 0;
      var radius = findInText(Sizes, item.description.text) || 9;
      var marker = addFireMarker(latlng, severity, radius, false);
      addInfoWindowToMarker(marker, createFireMarkerHtml(
          item));
    }
  }

  // Render approximate locations.
  var approximate_locations_length = approximate_locations.length;
  for (var key in approximate_locations) {
    mapApproximateLocation(approximate_locations[key])
  }

  // Update agency information
  //var agency_length = feed.agencys.length;
  //for (var i = 0; i < agency_length; ++i) {
    //var last_updated = timestampToFuzzyTime(feed.agencys[i].last_updated);
    //document.getElementById(feed.agencys[i].name).innerHTML +=
        //'(Last updated ' + last_updated + ')';
  //}
}

/*
 * Bind an info window to a marker.
 */
function addInfoWindowToMarker(marker, content) {
  // Create an info window to display above the marker.
  var infowindow = new google.maps.InfoWindow({
    content: content,
  });

  // Bind infowindow to marker click.
  google.maps.event.addListener(marker, 'click', function() {
    // Close existing info window.
    if (current_infowindow) {
      current_infowindow.close();
    }
    infowindow.open(map, marker);
    current_infowindow = infowindow;
  });
}

/*
 * Map an approximate location by ranking the data available for that
 * location.
 */
function mapApproximateLocation(loc) {
  var entries_length = loc.entries.length;
  var color;

  var severity = 0;
  var size = 9;
  var content = '<font style="font-size: smaller"/>';

  // Find the maximum severity and size recorded.
  for (var i = 0; i < entries_length; ++i) {
    var text = loc.entries[i].description.text;
    var entry_severity = findInText(Severity, text);
    var entry_size = findInText(Sizes, text);
    severity = Math.max(severity, entry_severity);
    radius = Math.max(size, entry_size);
    content += '<font color="#' + SeverityToColor[entry_severity] + '">' +
        '=================================</font><br/>' +
        text + '<br/><font color="#' + SeverityToColor[entry_severity] +
        '">=================================</font><br/></font>';
  }

  var marker = addFireMarker(loc.latlng, severity, size, true);

  // Create an info window to display above the marker.
  addInfoWindowToMarker(marker, content);
}

/*
 * Add a marker showing the location of a fire.
 */
function addFireMarker(latlng, severity, radius, approximate) {
  var letter = (approximate ? "A" : "%20");
  var image = 'http://chart.apis.google.com/chart?chst=d_map_pin_letter&' +
      'chld=' + letter + '|' + SeverityToColor[severity];

  var marker = new google.maps.Marker({
    position: latlng,
    map: map,
    icon: image
  });

  map_bounds.extend(latlng);
  return marker;
}

/*
 * Search for one of an array of search terms (keys) in a block of text,
 * and return the value associated with that key.
 */
function findInText(search_terms, text) {
  for (var term in search_terms) {
    if (text.indexOf(term) > -1) {
      return search_terms[term];
    }
  }
}

/*
 * Create formatted text to display in a fire marker infowindow.
 */
function createFireMarkerHtml(entry) {
  return '<font style="font-size: smaller" family="Arial, sans_serif"><a target="_blank" href="' +
      entry.link.text + '"><u>' + entry.title.text + '</u></a><br/>' +
      entry.description.text + '</br></font>';
}

/*
 * Convert a timestamp string to a fuzzy time.
 */
function timestampToFuzzyTime(timestamp) {
  var time = new Date(timestamp);
  var now = new Date().getTime();
  var delta = now - time;

  if (delta > SECONDS_IN_DAY) {
    return Math.floor(delta / SECONDS_IN_DAY) + " days ago";
  } else if (delta > SECONDS_IN_HOUR) {
    return Math.floor(delta / SECONDS_IN_HOUR) + " hours ago";
  } else {
    return Math.floor(delta / SECONDS_IN_MINUTE) + " minutes ago";
  }

  return "never";
}

/*
 * Search for a geo-location.
 */
function search() {
  var search_location = document.getElementById("search_text").value;

  geocoder = new google.maps.Geocoder();
  if (geocoder) {
    geocoder.geocode({'address': search_location}, showGeocodeResult);
  }

  hideInfo('search');
}

/*
 * Show a geocoded result.
 */
function showGeocodeResult(results, status) {
  if (status == google.maps.GeocoderStatus.OK) {
    map.setCenter(results[0].geometry.location);
  } else {
    alert("Couldn't find address.");
  }
}
